﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
using System.ServiceModel.Channels;
using System.Threading;
using System.Globalization;
using System.Reflection;
using System.ServiceProcess;
using System.Configuration;

namespace Laharsub.Server.Wcf
{
    static class Laharsub
    {
        static Dictionary<string, string> parameterDefaults = new Dictionary<string, string>()
        {
            { "baseAddress", "http://" + Environment.MachineName + "/ps/memory" },
            { "faultExceptionEnabled", "true" },
            { "helpEnabled", "true" },
            { "maxConcurrentCalls", Int32.MaxValue.ToString(CultureInfo.InvariantCulture) },
            { "serverPollTimeoutInS", "45" },
            { "InMemoryPubSubBackend.maxMemoryMessageStoreInMB", "10" },
            { "InMemoryPubSubBackend.maxMessageTtlInS", "15" },
            { "backendType", "Laharsub.Server.Wcf.InMemoryPubSubBackend" }
        };

        internal static IPubSubBackend Backend { get; set; }
        static WebServiceHost sh;

        internal static string GetParamAsString(string name)
        {
            string result = ConfigurationManager.AppSettings.Get(name);
            if (string.IsNullOrEmpty(result))
            {
                result = parameterDefaults[name];
            }
            else if ("baseAddress".Equals(name))
            {
                result = result.Replace("{MachineName}", Environment.MachineName);
            }
            return result;
        }

        internal static int GetParamAsInt(string name)
        {
            return Int32.Parse(GetParamAsString(name), CultureInfo.InvariantCulture);
        }

        internal static bool GetParamAsBool(string name)
        {
            return "true".Equals(GetParamAsString(name), StringComparison.InvariantCultureIgnoreCase);
        }

        public static void Start()
        {
            Console.WriteLine("Starting with following parameters:");
            foreach (string key in parameterDefaults.Keys)
            {
                Console.WriteLine("   {0}:{1}", key, Laharsub.GetParamAsString(key));
            }

            try
            {
                sh = new WebServiceHost(
                    typeof(PubSubService),
                    new Uri(Laharsub.GetParamAsString("baseAddress")));
                ServiceEndpoint endpoint = sh.AddServiceEndpoint(
                    typeof(PubSubService),
                    new WebHttpBinding(),
                    "");
                ServiceBehaviorAttribute sb = new ServiceBehaviorAttribute
                {
                    ConcurrencyMode = ConcurrencyMode.Multiple,
                    InstanceContextMode = InstanceContextMode.Single,
                    AddressFilterMode = AddressFilterMode.Any
                };
                ServiceThrottlingBehavior stb = new ServiceThrottlingBehavior
                {
                    MaxConcurrentCalls = Laharsub.GetParamAsInt("maxConcurrentCalls")
                };
                WebHttpBehavior whb = new WebHttpBehavior
                {
                    HelpEnabled = Laharsub.GetParamAsBool("helpEnabled"),
                    FaultExceptionEnabled = Laharsub.GetParamAsBool("faultExceptionEnabled")
                };
                if (sh.Description.Behaviors.Contains(typeof(ServiceBehaviorAttribute)))
                {
                    sh.Description.Behaviors.Remove(typeof(ServiceBehaviorAttribute));
                }
                sh.Description.Behaviors.Add(sb);
                sh.Description.Behaviors.Add(stb);
                endpoint.Behaviors.Add(whb);

                sh.Open();
            }
            catch (Exception)
            {
                sh = null;
                throw;
            }
        }

        public static void Stop()
        {
            if (sh != null)
            {
                sh.Close();
                sh = null;
            }
        }

        static void InitializeBackend()
        {
            try
            {
                Type bt = Type.GetType(Laharsub.GetParamAsString("backendType"), true, true);
                if (!typeof(IPubSubBackend).IsAssignableFrom(bt))
                {
                    throw new InvalidOperationException("The CLR type of the pub/sub backend implementation must implement Laharsub.Server.Wcf.IPubSubBackend interface.");
                }
                ConstructorInfo c = bt.GetConstructor(new Type[] {});
                if (c != null)
                {
                    Laharsub.Backend = (IPubSubBackend)c.Invoke(null);
                }
                else
                {
                    throw new InvalidOperationException("The CLR type of the pub/sub backend must have a public parameterless constructor or a public constructor.");
                }
            }
            catch (Exception e)
            {
                throw new InvalidOperationException("Cannot create an instance of the pub/sub backend.", e);
            }
        }

        static void Main(string[] args)
        {
            try
            {
                Laharsub.InitializeBackend();
                if (args.Length == 1 && "/console".Equals(args[0], StringComparison.InvariantCultureIgnoreCase))
                {
                    // console application
                    Laharsub.Start();
                    Console.WriteLine("Service started. Ctrl-C to terminate.");
                    using (ManualResetEvent e = new ManualResetEvent(false))
                    {
                        e.WaitOne();
                    }
                }
                else
                {
                    // NT service - default
                    ServiceBase[] servicesToRun;
                    servicesToRun = new ServiceBase[] { new LaharsubService() };
                    ServiceBase.Run(servicesToRun);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
    }
}
